/*
 * Created on Feb 17, 2005
 * Created by dfhuynh
 */
package edu.mit.simile.longwell.query.project;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.NodeIterator;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.ResIterator;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.ResourceFactory;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.vocabulary.RDF;

import edu.mit.simile.longwell.Profile;

/**
 * @author dfhuynh
 */
abstract public class PropertyProjection extends ProjectionBase {
	final protected Profile		m_profile;
	final protected Property	m_property;
	final protected boolean		m_forward;
	final protected Set 		m_objects;
	
	/**
	 * @param locale
	 */
	public PropertyProjection(
			Profile 	profile, 
			Property 	property, 
			boolean 	forward, 
			String 		locale,
			Set			objects) {
		super(locale);
		m_profile = profile;
		m_property = property;
		m_forward = forward;
		m_objects = objects;
	}

	/* (non-Javadoc)
	 * @see edu.mit.simile.longwell.query.project.IProjection#getUniqueness()
	 */
	public float getUniqueness() {
		return m_profile.getSchemaModel()
			.getStructuredProperty(m_property).getUniqueness();
	}
	
	/* (non-Javadoc)
	 * @see edu.mit.simile.longwell.query.project.IProjection#getObjects()
	 */
	public Set getObjects() {
		if (m_objects != null) {
			return m_objects;
		} else {
			return m_profile.getSchemaModel().getAllObjects();
		}
	}
	
	/* (non-Javadoc)
	 * @see edu.mit.simile.longwell.query.IProjection#getValues()
	 */
	public Set getValues() {
		if (m_objects != null) {
			return super.getValues();
		} else {
			/**
			 * An optimization for root projection
			 */
			if (m_values == null) {
				m_values = new HashSet();
				
				NodeIterator ni = m_profile.getModel()
					.listObjectsOfProperty(m_property);
	
				while (ni.hasNext()) {
					m_values.add(ni.next());
				}
			}
			return m_values;
		}
	}
	
	/* (non-Javadoc)
	 * @see edu.mit.simile.longwell.query.project.ProjectionBase#internalGetValue(com.hp.hpl.jena.rdf.model.Resource)
	 */
	protected Object internalGetValue(Resource object) {
		if (m_forward) {
			Statement stmt = 
				m_profile.getModel().getProperty(object, m_property);
			
			if (stmt != null) {
				return nodeToValue(stmt.getObject());
			}
		} else {
			ResIterator ri = 
				m_profile.getModel().listSubjectsWithProperty(
					m_property, object);
			
			if (ri.hasNext()) {
				return nodeToValue(ri.nextResource());
			}
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see edu.mit.simile.longwell.query.project.ProjectionBase#internalGetObjects(java.lang.Object)
	 */
	protected Set internalGetObjects(Object value) {
		if (value != null) {
			Set objects = new HashSet();
			
			Iterator i = iterateObjectsWithValue(value);
			while (i.hasNext()) {
				Resource r = (Resource) i.next();
				if (m_objects == null || m_objects.contains(r)) {
					objects.add(r);
				}
			}
			
			return objects;
		} else {
			Set objects = new HashSet(getObjects());
			
			Iterator i = m_forward ?
				(Iterator) m_profile.getModel().listSubjectsWithProperty(m_property) :
				(Iterator) m_profile.getModel().listObjectsOfProperty(m_property);
			
			while (i.hasNext()) {
				objects.remove(i.next());
			}
			
			return objects;
		}
	}
	
	protected Iterator iterateObjectsWithValue(Object value) {
		Model model = m_profile.getModel();
		Resource resource = model.getResource(value.toString());
		//filter for classes
		/*
		if(m_property.toString().equals(RDF.type.toString()) || m_property.isResource()){
			ArrayList<Resource> list = new ArrayList<Resource>();
			StmtIterator si = model.listStatements(null, m_property, (RDFNode) resource);
			if(!si.hasNext()){
				//return m_profile.getModel().listSubjectsWithProperty(m_property, (String) value.toString());
				return m_profile.getModel().listSubjectsWithProperty(m_property, resource);
			}
			else{
				int count = 0;
				while (si.hasNext()) {
					Statement s = si.nextStatement();
					Resource r = s.getSubject();
					Object obj = s.getObject();
					//if(obj.toString().equals(value.toString()))
						list.add(r);
						count++;
					
				}
				return list.iterator();
			}
		}
		else{*/
			if (m_forward) {
				//if(resource.toString().startsWith("http://"))
				if(value instanceof Resource)
					return m_profile.getModel().listSubjectsWithProperty(m_property, resource);
				else
					return m_profile.getModel().listSubjectsWithProperty(m_property, resource.toString());
			} else {
				return m_profile.getModel()
					.listObjectsOfProperty((Resource) value, m_property);
			}	
		//}
	}

	/* (non-Javadoc)
	 * @see edu.mit.simile.longwell.query.project.ProjectionBase#getObjectToValueMap()
	 */
	protected void fillObjectToValueMap(Map m) {
		StmtIterator si = m_profile.getModel()
			.listStatements(null, m_property, (RDFNode) null);
		
		if (m_forward) {
			while (si.hasNext()) {
				Statement s = si.nextStatement();
				Resource r = s.getSubject();
				m.put(r, nodeToValue(s.getObject()));
			}
		} else {
			while (si.hasNext()) {
				Statement s = si.nextStatement();
				RDFNode n = s.getObject();
				
				if (n instanceof Resource) {
					m.put(n, nodeToValue(s.getObject()));
				}
			}
		}
	}
	
	protected Object nodeToValue(RDFNode node) {
		return node;
	}
	public String getProperty(){
		return m_property.toString();
	}
}
